/*
 * Copyright (C) 2019 ByteDance Inc
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.bytedance.scene;


import com.bytedance.scene.navigation.NavigationScene;
import com.bytedance.scene.navigation.NavigationSceneOptions;
import com.bytedance.scene.utlity.SceneInstanceUtility;
import com.bytedance.scene.utlity.ThreadUtility;
import com.bytedance.scene.utlity.Utility;
import ohos.aafwk.ability.Ability;
import ohos.aafwk.ability.fraction.Fraction;
import ohos.aafwk.ability.fraction.FractionAbility;
import ohos.aafwk.ability.fraction.FractionManager;
import ohos.aafwk.ability.fraction.FractionScheduler;
import ohos.aafwk.content.Intent;
import ohos.aafwk.content.IntentParams;

import java.util.Optional;

/**
 * Created by JiangQi on 7/30/18.
 */
public final class NavigationSceneUtility {
    private static final String LIFE_CYCLE_FRAGMENT_TAG = GroupSceneUtility.LIFE_CYCLE_FRAGMENT_TAG;

    private NavigationSceneUtility() {
    }

    public static final class Builder {

        private final FractionAbility mAbility;

        private final Class<? extends Scene> mRootSceneClazz;

        private IntentParams mRootSceneArguments;
        private boolean mDrawWindowBackground = true;
        private boolean mFixSceneBackgroundEnabled = true;

        private int mSceneBackgroundResId = 0;

        private int mIdRes = ResourceTable.Id_content;
        private boolean mSupportRestore = false;

        private SceneComponentFactory mRootSceneComponentFactory;

        private String mTag = LIFE_CYCLE_FRAGMENT_TAG;
        private boolean mImmediate = true;

        private Builder(FractionAbility ability, Class<? extends Scene> rootSceneClazz) {
            this.mAbility = Utility.requireNonNull(ability, "Ability can't be null");
            this.mRootSceneClazz = Utility.requireNonNull(rootSceneClazz, "Root Scene class can't be null");
        }


        public Builder rootSceneArguments(IntentParams rootSceneArguments) {
            this.mRootSceneArguments = rootSceneArguments;
            return this;
        }

        public Builder toView(int idRes) {
            this.mIdRes = idRes;
            return this;
        }


        public Builder supportRestore(boolean supportRestore) {
            this.mSupportRestore = supportRestore;
            return this;
        }


        public Builder rootSceneComponentFactory(SceneComponentFactory rootSceneComponentFactory) {
            this.mRootSceneComponentFactory = rootSceneComponentFactory;
            return this;
        }


        public Builder drawWindowBackground(boolean drawWindowBackground) {
            this.mDrawWindowBackground = drawWindowBackground;
            return this;
        }


        public Builder fixSceneWindowBackgroundEnabled(boolean fixSceneBackground) {
            this.mFixSceneBackgroundEnabled = fixSceneBackground;
            return this;
        }


        public Builder sceneBackground(int resId) {
            this.mSceneBackgroundResId = resId;
            return this;
        }


        public Builder tag(String tag) {
            this.mTag = Utility.requireNonNull(tag, "Tag can't be null");
            return this;
        }


        public Builder immediate(boolean immediate) {
            this.mImmediate = immediate;
            return this;
        }


        public SceneDelegate build() {
            NavigationSceneOptions navigationSceneOptions = new NavigationSceneOptions(this.mRootSceneClazz, this.mRootSceneArguments);
            navigationSceneOptions.setDrawWindowBackground(this.mDrawWindowBackground);
            navigationSceneOptions.setFixSceneWindowBackgroundEnabled(this.mFixSceneBackgroundEnabled);
            navigationSceneOptions.setSceneBackground(this.mSceneBackgroundResId);
            return setupWithAbility(this.mAbility, this.mIdRes, navigationSceneOptions, this.mRootSceneComponentFactory, this.mSupportRestore, this.mTag, this.mImmediate);
        }
    }


    public static Builder setupWithAbility(final FractionAbility ability,
                                            Class<? extends Scene> rootScene) {
        return new Builder(ability, rootScene);
    }

    @Deprecated
    public static SceneDelegate setupWithAbility(final FractionAbility ability, Intent savedInstanceState,
                                                  Class<? extends Scene> rootScene,
                                                  boolean supportRestore) {
        return setupWithAbility(ability, savedInstanceState,
                new NavigationSceneOptions(rootScene, null), null, supportRestore);
    }

    @Deprecated
    public static SceneDelegate setupWithAbility(final FractionAbility ability, Intent savedInstanceState,
                                                  Class<? extends Scene> rootScene,
                                                  SceneComponentFactory rootSceneComponentFactory,
                                                  boolean supportRestore) {
        return setupWithAbility(ability, savedInstanceState,
                new NavigationSceneOptions(rootScene, null), rootSceneComponentFactory, supportRestore);
    }

    @Deprecated
    public static SceneDelegate setupWithAbility(final FractionAbility ability, Intent savedInstanceState,
                                                  NavigationSceneOptions navigationSceneOptions,
                                                  boolean supportRestore) {
        return setupWithAbility(ability, ResourceTable.Id_content, savedInstanceState, navigationSceneOptions, null, supportRestore);
    }

    @Deprecated
    public static SceneDelegate setupWithAbility(final FractionAbility ability, Intent savedInstanceState,
                                                  NavigationSceneOptions navigationSceneOptions,
                                                  SceneComponentFactory rootSceneComponentFactory,
                                                  boolean supportRestore) {
        return setupWithAbility(ability, ResourceTable.Id_content, savedInstanceState, navigationSceneOptions, rootSceneComponentFactory, supportRestore);
    }

    @Deprecated
    public static SceneDelegate setupWithAbility(final FractionAbility ability,
                                                  int idRes,
                                                  Intent savedInstanceState,
                                                  NavigationSceneOptions navigationSceneOptions,
                                                  SceneComponentFactory rootSceneComponentFactory,
                                                  final boolean supportRestore) {
        return setupWithAbility(ability, idRes, savedInstanceState, navigationSceneOptions,
                rootSceneComponentFactory, supportRestore, LIFE_CYCLE_FRAGMENT_TAG, true);
    }

    @Deprecated
    public static SceneDelegate setupWithAbility(final FractionAbility ability,
                                                  int idRes,
                                                  Intent savedInstanceState,
                                                  NavigationSceneOptions navigationSceneOptions,
                                                  SceneComponentFactory rootSceneComponentFactory,
                                                  final boolean supportRestore,
                                                  String tag,
                                                  boolean immediate) {
        return setupWithAbility(ability, idRes, navigationSceneOptions, rootSceneComponentFactory, supportRestore, tag, immediate);
    }


    private static SceneDelegate setupWithAbility(final FractionAbility ability,
                                                   int idRes,
                                                   NavigationSceneOptions navigationSceneOptions,
                                                   SceneComponentFactory rootSceneComponentFactory,
                                                   final boolean supportRestore,
                                                   String tag,
                                                   boolean immediate) {
        ThreadUtility.checkUIThread();
        if (tag == null) {
            throw new IllegalArgumentException("tag cant be null");
        }
        checkDuplicateTag(ability, tag);

        final NavigationScene navigationScene = (NavigationScene) SceneInstanceUtility.getInstanceFromClass(NavigationScene.class,
                navigationSceneOptions.toBundle());
        if (!Utility.isAbilityStatusValid(ability)) {
            return new DestroyedSceneDelegate(navigationScene);
        }
        navigationScene.setRootSceneComponentFactory(rootSceneComponentFactory);

        FractionManager fractionManager = ability.getFractionManager();
        Optional<Fraction> fractionByTag = fractionManager.getFractionByTag(tag);
        LifeCycleFragment lifeCycleFragment;

        if (!supportRestore&&fractionByTag.isPresent()) {
            lifeCycleFragment = (LifeCycleFragment) fractionByTag.get();
            FractionScheduler fractionScheduler = fractionManager.startFractionScheduler();
            fractionScheduler.remove(lifeCycleFragment);
            Utility.commitFragment(fractionManager, fractionScheduler, immediate);
            lifeCycleFragment = null;
        }

        ViewFinder viewFinder = new AbilityViewFinder(ability);

        ScopeHolderFragment targetScopeHolderFragment = null;
        SceneLifecycleDispatcher<NavigationScene> dispatcher = null;

        if (fractionByTag.isPresent()) {
            lifeCycleFragment = (LifeCycleFragment) fractionByTag.get();
            final ScopeHolderFragment scopeHolderFragment = ScopeHolderFragment.install(idRes, ability, tag, false, immediate);
            dispatcher = new SceneLifecycleDispatcher<>(idRes, viewFinder, navigationScene, scopeHolderFragment, supportRestore);
            lifeCycleFragment.setSceneContainerLifecycleCallback(dispatcher);
            targetScopeHolderFragment = scopeHolderFragment;
        } else {
            lifeCycleFragment = LifeCycleFragment.newInstance(supportRestore);
            FractionScheduler fractionScheduler = fractionManager.startFractionScheduler();
            fractionScheduler.add(idRes, lifeCycleFragment, tag);

            final ScopeHolderFragment scopeHolderFragment = ScopeHolderFragment.install(idRes, ability, tag, !supportRestore, immediate);
            dispatcher = new SceneLifecycleDispatcher<>(idRes, viewFinder, navigationScene, scopeHolderFragment, supportRestore);
            lifeCycleFragment.setSceneContainerLifecycleCallback(dispatcher);

            Utility.commitFragment(fractionManager, fractionScheduler, immediate);
            targetScopeHolderFragment = scopeHolderFragment;
        }
        final LifeCycleFragmentSceneDelegate delegate = new LifeCycleFragmentSceneDelegate(ability, navigationScene, lifeCycleFragment, targetScopeHolderFragment, immediate);
        return delegate;
    }

    private static void checkDuplicateTag(Ability ability, String tag) {
        GroupSceneUtility.checkDuplicateTag(ability, tag);
    }

    static void removeTag(Ability ability, String tag) {
        GroupSceneUtility.removeTag(ability, tag);
    }
}